# -*- coding: utf-8 -*-

"""
 (c) 2023 - Copyright CTyunOS Inc

 Authors:
   youyifeng <youyf2@chinatelecom.cn>

"""
import logging

from cve_ease.helper import notifier_timestamp

from .base import NotifierBase

logger = logging.getLogger('cve-ease')


class Mail163(NotifierBase):
    """
    Mail163 notifier
    """

    def __init__(self, gconfig):
        self._name = 'mail163_notifier'
        self._config = gconfig
        # setting wanip
        if not hasattr(gconfig, 'WANIP'):
            from cve_ease.helper import get_wanip
            self._wanip = get_wanip()
        else:
            self._wanip = gconfig.WANIP
        # setting watcher
        self._watcher = None
        if hasattr(gconfig, 'WATCHER'):
            self._watcher = gconfig.WATCHER
        # setting notify timestamp
        self._notify_time = notifier_timestamp()

    def do_send(self, data, key: list):
        import smtplib
        from email.mime.text import MIMEText

        subject = f"Msg from CVE-EASE"
        # sender = self._mail_sender
        # recver = self._mail_recver
        # password = self._mail_smtp_token
        sender, recver, password = key[0], key[1], key[2]
        content = data
        message = MIMEText(content, "plain", "utf-8")

        message['Subject'] = subject
        message['To'] = recver
        message['From'] = sender

        try:
            smtp = smtplib.SMTP_SSL("smtp.163.com", 994)
            smtp.login(sender, password)
            smtp.sendmail(sender, [recver], message.as_string())
            smtp.close()
        except Exception as e:
            logger.error(str(e))
            logger.error(f'[-] {self._name} {key} send mesg failed!')
            exit(1)
        logger.debug(f'[+] {self._name} {key} send mesg success!')

    def send_text(self, msg="", key=None):
        self.do_send(msg, key)

    def get_info_column(self, info_type: str):
        """
        获取信息字段，用于导出csv
        :param info_type:
        :return:
        """
        if "cve" == info_type:
            return CVE.COLUMN
        elif "sa" == info_type:
            return SA.COLUMN
        else:
            raise f" no column for {info_type} "

    def check_mail_token(self, info_list: list):
        for i, k in enumerate(info_list):
            info_list[i] = info_list[i].strip()
            if '' == info_list[i]:
                return []
        return info_list

    def do_update_notify(self, info_diff: dict, info_type: str):
        """
        更新播报
        :param info_diff:
        :param info_type:
        :return:
        """
        for w in self._watcher:
            cw = self._watcher[w]
            if self._name not in cw:
                continue
            if len(cw[self._name]) < 3 and '' == cw[self._name][0]:
                continue

            # check notifier token ,no key should be empty
            notifier_token = check_mail_token(cw[self._name][:3])
            if not notifier_token:
                continue

            match = {}
            flag = False

            filter_pkglist = cw['watch_pkg'] if 'watch_pkg' in cw else []
            if 'ALL' in filter_pkglist:
                match = info_diff
                flag = True
            filter_score = cw['watch_score'] if 'watch_score' in cw else 0.0
            if filter_score <= 0.0:
                match = info_diff
                flag = True

            if not match:
                for status in info_diff:
                    if status not in match:
                        match[status] = []
                    for index, cve in enumerate(filter_diff[status]):
                        if "packageName" in cve and "" != cve["packageName"]:
                            for fpkg in filter_pkglist:
                                if fpkg in filter_pkglist:
                                    flag = True
                                    match[status].append(cve)
                        elif "cvsssCoreNVD" in cve \
                                and "" != cve["cvsssCoreNVD"] \
                                and float(cve["cvsssCoreNVD"]) >= filter_score:
                            flag = True
                            match[status].append(cve)
                        elif "cvsssCoreOE" in cve \
                                and "" != cve["cvsssCoreOE"] \
                                and float(cve["cvsssCoreOE"]) >= filter_score:
                            flag = True
                            match[status].append(cve)
            if not flag:
                logger.debug(" no update need notify")
                return
            self.send_text(self.pretty_update_info(info_type, match), key=notifier_token)
        logger.debug(f" * {self._name} do_update_notify done!")

    def do_status_notify(self, info_list: list, info_type: str):
        """
        状态播报
        :param info_list:
        :param info_type:
        :return:
        """
        for w in self._watcher:
            cw = self._watcher[w]
            if self._name not in cw:
                continue
            if len(cw[self._name]) < 2 and '' == cw[self._name][1]:
                continue

            notifier_token = cw[self._name][1]
            match = []
            flag = False

            filter_pkglist = cw['watch_pkg'] if 'watch_pkg' in cw else []
            if 'ALL' in filter_pkglist:
                match = info_list
                flag = True

            filter_score = cw['watch_score'] if 'watch_score' in cw else 0.0
            if filter_score <= 0.0:
                match = info_list
                flag = True

            if not match:
                for index, cve in enumerate(info_list):
                    if "packageName" in cve and "" != cve["packageName"]:
                        for fpkg in filter_pkglist:
                            if fpkg in filter_pkglist:
                                flag = True
                                match.append(cve)
                    elif "cvsssCoreNVD" in cve \
                            and "" != cve["cvsssCoreNVD"] \
                            and float(cve["cvsssCoreNVD"]) >= filter_score:
                        flag = True
                        match.append(cve)
                    elif "cvsssCoreOE" in cve \
                            and "" != cve["cvsssCoreOE"] \
                            and float(cve["cvsssCoreOE"]) >= filter_score:
                        flag = True
                        match.append(cve)

            logger.debug(
                f" * {self._name} do_status_nofity {w} info:{info_type} filter:{filter_pkglist} len:{len(match)}"
            )

            if flag and self._name in cw and 0 != len(match):
                info = self.pretty_status_info(info_type, match)
                self.send_text(msg=info, key=notifier_token)
        logger.debug(f" * {self._name} do_status_notify done!")

    def pretty_status_info(self, info_type: str, list_info: list):
        """
        格式化状态播报信息，多态
        :param info_type:
        :param list_info:
        :return:
        """
        pretty_func_name = f"do_{info_type}_status_pretty"
        if super().hasFunction(pretty_func_name):
            return getattr(self, pretty_func_name)(list_info)
        else:
            logger.debug(f" * no func {pretty_func_name} found in {self.__class__.__name__}")
            return ""

    def pretty_update_info(self, info_type: str, dict_info: dict):
        """
        格式化更新播报信息，多态
        :param info_type:
        :param dict_info:
        :return:
        """
        pretty_func_name = f"do_{info_type}_update_pretty"
        if super().hasFunction(pretty_func_name):
            return getattr(self, pretty_func_name)(dict_info)
        else:
            logger.debug(f" * no func {pretty_func_name} found in {self.__class__.__name__}")
            return ""

    def per_cve_info_pretty(self, record):
        """
        每CVE信息格式化
        :param record:
        :return:
        """
        return (
                "评分:[{}] {}\n" +
                " 包名: {}\n" +
                " 发布时间: {}\n" +
                " https://www.openeuler.org/zh/security/cve/detail/?cveId={}&packageName={}\n"
        ).format(
            record["cvsssCoreNVD"] if record["cvsssCoreNVD"] else record["cvsssCoreOE"],
            record["cveId"],
            record["packageName"],
            record["updateTime"],
            record["cveId"],
            record["packageName"],
        )

    def do_cve_status_pretty(self, list_info: list):
        """
        CVE信息 状态播报 格式化
        :param list_info:
        :return:
        """
        message = (
                "### Msg from CVE-EASE: \n" +
                " CVE安全公告 - 状态播报\n" +
                " {}\n" +
                "服务状态: **<font color=\"info\">OK</font>**\n" +
                "服务IP: **<font color=\"info\">{}</font>**\n" +
                "CVE记录总数: **<font color=\"red\">{}</font>**\n" +
                "更新情况:\n" +
                "Top**{}**信息概况:\n" +
                "\n"
        ).format(
            self._notify_time,
            self._wanip,
            len(list_info),
            self._config.NOTIFIER_RECORD_NUM
        )
        count = int(self._config.NOTIFIER_RECORD_NUM)
        for r in list_info[:count]:
            message += self.per_cve_info_pretty(r)

        message += """\n**相关同事请务必及时修复漏洞，杜绝隐患保障系统安全。**\n"""
        return message

    def do_cve_update_pretty(self, dict_info: dict):
        """
        CVE信息 更新播报 格式化
        :param dict_info:
        :return:
        """

        message = (
                "Msg from CVE-EASE: \n" +
                " CVE安全公告 - 更新播报\n" +
                " {}\n" +
                "服务状态: OK\n" +
                "服务IP: {}\n" +
                "CVE记录总数: {}\n" +
                "更新情况:\n" +
                " 新增:{}" +
                " 修改:{}" +
                " 删减:{}\n" +
                "Top{}变更信息概况:\n" +
                "\n"
        ).format(
            self._notify_time,
            self.wanip,
            len(dict_info["data"]),
            len(dict_info["add"]),
            len(dict_info["modify"]),
            len(dict_info["delete"]),
            self._config.NOTIFIER_RECORD_NUM
        )
        count = int(self._config.NOTIFIER_RECORD_NUM)
        addlist = sorted(diff["add"], key=lambda d: d['id'])
        modifylist = sorted(diff["modify"], key=lambda d: d['id'])
        dellist = sorted(diff["delete"], key=lambda d: d['id'])

        need_output_record_list = addlist + modifylist + dellist
        for r in need_output_record_list[:count]:
            message += self.per_cve_info_pretty(r)

        message += """\n相关同事请务必及时修复漏洞，杜绝隐患保障系统安全。\n"""
        return message

    def per_sa_info_pretty(self, record):
        """
        每SA信息 格式化
        :param record:
        :return:
        """
        return (
                "编号:{}\n" +
                " 包名: {}\n" +
                " CVEID: {}\n" +
                " 发布时间: {}\n" +
                " https://www.openeuler.org/zh/security/safety-bulletin/detail/?id={}\n"
        ).format(
            record["securityNoticeNo"],
            record["affectedComponent"],
            record["cveId"],
            record["updateTime"],
            record["securityNoticeNo"],
        )

    def do_sa_status_pretty(self, list_info: list):
        """
        SA信息 状态播报格式化
        :param list_info:
        :return:
        """
        message = (
                "### Msg from CVE-EASE: \n" +
                " SA安全公告 - 状态播报\n" +
                " {}\n" +
                "服务状态: **<font color=\"info\">OK</font>**\n" +
                "服务IP: **<font color=\"info\">{}</font>**\n" +
                "SA记录总数: **<font color=\"red\">{}</font>**\n" +
                "更新情况:\n" +
                "Top**{}**信息概况:\n" +
                "\n"
        ).format(
            self._notify_time,
            self._wanip,
            len(list_info),
            self._config.NOTIFIER_RECORD_NUM
        )
        count = int(self._config.NOTIFIER_RECORD_NUM)
        for r in list_info[:count]:
            message += self.per_sa_info_pretty(r)

        message += """\n**相关同事请务必及时修复漏洞，杜绝隐患保障系统安全。**\n"""
        return message

    def do_sa_update_pretty(self, dict_info: dict):
        """
        SA信息 更新播报 格式化
        :param dict_info:
        :return:
        """
        message = (
                "Msg from CVE-EASE: \n" +
                " SA安全公告 - 更新播报\n" +
                " {}\n" +
                "服务状态: OK\n" +
                "服务IP: {}\n" +
                "SA记录总数: {}\n" +
                "更新情况:\n" +
                " 新增:{}" +
                " 修改:{}" +
                " 删减:{}\n" +
                "Top{}变更信息概况:\n" +
                "\n"
        ).format(
            self._notify_time,
            self._wanip,
            len(dict_info["data"]),
            len(dict_info["add"]),
            len(dict_info["modify"]),
            len(dict_info["delete"]),
            self._config.NOTIFIER_RECORD_NUM
        )
        count = int(self._config.NOTIFIER_RECORD_NUM)
        addlist = sorted(dict_info["add"], key=lambda d: d['id'])
        modifylist = sorted(dict_info["modify"], key=lambda d: d['id'])
        dellist = sorted(dict_info["delete"], key=lambda d: d['id'])

        need_output_record_list = addlist + modifylist + dellist
        for r in need_output_record_list[:count]:
            message += self.per_sa_info_pretty(r)

        message += """\n**相关同事请务必及时修复漏洞，杜绝隐患保障系统安全。**\n"""
        return message
